feat: add trend visualization with sparklines to perf report#9939
feat: add trend visualization with sparklines to perf report#9939christian-byrne merged 3 commits intomainfrom
Conversation
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ❌ 622 passed, 1 failed · 5 flaky❌ Failed Tests📊 Browser Reports
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds sparkline/trend utilities and integrates trend visuals into perf reports; removes two legacy PR workflow files and introduces a unified PR report workflow plus CI tweaks that record head SHAs and fetch historical perf baselines; adds a unified-report generator combining size and perf outputs. Changes
Sequence DiagramsequenceDiagram
participant GHA as GitHub Actions
participant Repo as Repo (artifacts)
participant PerfData as perf-data branch
participant Unified as scripts/unified-report.js
participant PerfReport as scripts/perf-report.ts
participant PerfStats as scripts/perf-stats.ts
participant Output as PR Comment
GHA->>Repo: locate size & perf workflow run artifacts (by head SHA)
GHA->>PerfData: attempt fetch/download historical perf archive (optional)
GHA->>Unified: run unified-report with size/perf statuses
activate Unified
Unified->>PerfReport: invoke perf-report (when perf ready)
activate PerfReport
PerfReport->>PerfData: read historical metric files (if present)
PerfReport->>PerfStats: compute sparkline(timeSeries)
PerfReport->>PerfStats: compute trendDirection(timeSeries)
PerfReport->>PerfStats: compute trendArrow(direction)
PerfReport-->>Unified: rendered perf section (with trends if ≥3 points)
deactivate PerfReport
Unified->>Output: post combined report as PR comment
deactivate Unified
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Comment |
4068979 to
b2d31dc
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
scripts/perf-stats.ts (1)
86-104: Consider the zero-baseline edge case.When
firstMean === 0butsecondMean > 0, the function returns'stable'to avoid division by zero. This is a safe fallback, but it masks what could be a significant upward trend (e.g., a metric that was previously zero and is now non-zero).If this scenario is unlikely for the performance metrics being tracked, the current behavior is acceptable. Otherwise, consider treating it as
'rising'whensecondMean > 0andfirstMean === 0.Optional: Handle zero-baseline as rising
- if (firstMean === 0) return 'stable' + if (firstMean === 0) return secondMean > 0 ? 'rising' : 'stable'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/perf-stats.ts` around lines 86 - 104, The current trendDirection function returns 'stable' when firstMean === 0 to avoid division by zero, which hides cases where secondMean > 0; update trendDirection to explicitly handle the zero-baseline: if firstMean === 0 and secondMean > 0 return 'rising' (and if both are 0 keep 'stable'), otherwise compute changePct as before using ((secondMean - firstMean) / firstMean) * 100 to determine 'rising'/'falling'/'stable' while avoiding division by zero; refer to the function trendDirection and the variables firstMean, secondMean, and changePct when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@scripts/perf-stats.ts`:
- Around line 86-104: The current trendDirection function returns 'stable' when
firstMean === 0 to avoid division by zero, which hides cases where secondMean >
0; update trendDirection to explicitly handle the zero-baseline: if firstMean
=== 0 and secondMean > 0 return 'rising' (and if both are 0 keep 'stable'),
otherwise compute changePct as before using ((secondMean - firstMean) /
firstMean) * 100 to determine 'rising'/'falling'/'stable' while avoiding
division by zero; refer to the function trendDirection and the variables
firstMean, secondMean, and changePct when making the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: daf48065-805c-4f22-b1c5-8ad9d68733b1
📒 Files selected for processing (4)
.github/workflows/pr-perf-report.yamlscripts/perf-report.tsscripts/perf-stats.test.tsscripts/perf-stats.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- scripts/perf-stats.test.ts
benceruleanlu
left a comment
There was a problem hiding this comment.
feel free to (force) merge and iterate, but it will need to change with #9911, and PTAL:
the new history download path is incompatible with the current perf-data branch contents. I verified origin/perf-data currently contains baselines/perf-*.json, while this change archives perf-history/ and the loader only reads temp/perf-history/
/perf-metrics.json.
- Add history download step to unified pr-report.yaml reading from baselines/ on perf-data branch (the actual layout) - Fix loadHistoricalReports to handle flat JSON files, not just subdirectories with perf-metrics.json - Handle zero-baseline edge case in trendDirection: return 'rising' when firstMean=0 but secondMean>0 instead of masking as 'stable' Addresses review feedback: #9939 (review)
a712634 to
0f3ea48
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
|
fixed in 0f3ea48 - stacked this PR on #9911 (which replaces pr-perf-report.yaml with pr-report.yaml), added a history download step that reads from baselines/ using git ls-tree + git show, and updated loadHistoricalReports() to handle flat .json files. also fixed the zero-baseline edge case coderabbit flagged. ready for re-review @benceruleanlu |
- Add sparkline(), trendDirection(), trendArrow() to perf-stats.ts - Add collapsible 'Trend' section to perf-report.ts showing ASCII sparklines and directional arrows for each metric over last N commits on main - Add historical data download step to pr-perf-report.yaml from perf-data orphan branch - Switch pr-perf-report.yaml to setup-frontend action and pnpm exec - Add tests for all new functions (sparkline, trendDirection, trendArrow)
- Add history download step to unified pr-report.yaml reading from baselines/ on perf-data branch (the actual layout) - Fix loadHistoricalReports to handle flat JSON files, not just subdirectories with perf-metrics.json - Handle zero-baseline edge case in trendDirection: return 'rising' when firstMean=0 but secondMean>0 instead of masking as 'stable' Addresses review feedback: #9939 (review)
0f3ea48 to
5b9e71e
Compare
📦 Bundle: 5 MB gzip 🔴 +126 BDetailsSummary
Category Glance App Entry Points — 22.7 kB (baseline 22.7 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.1 MB (baseline 1.1 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 75.9 kB (baseline 75.9 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 2 unchanged Panels & Settings — 461 kB (baseline 461 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 12 unchanged User & Accounts — 16.9 kB (baseline 16.9 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 5 added / 5 removed / 2 unchanged Editors & Dialogs — 82 kB (baseline 82 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 2 added / 2 removed UI Components — 59.3 kB (baseline 59.3 kB) • ⚪ 0 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 2.91 MB (baseline 2.91 MB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 318 kB (baseline 318 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 14 added / 14 removed / 8 unchanged Vendor & Third-Party — 9.78 MB (baseline 9.78 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 8.24 MB (baseline 8.24 MB) • ⚪ 0 BBundles that do not match a named category
Status: 53 added / 53 removed / 79 unchanged ⚡ Performance Report
Raw data{
"timestamp": "2026-03-17T13:25:21.368Z",
"gitSha": "92a6eef3d8f4a8299bbfaaee79134ef1368776ab",
"branch": "feat/perf-trend-visualization",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2004.9300000000017,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.232000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 360.44100000000003,
"heapDeltaBytes": 1604708,
"domNodes": 22,
"jsHeapTotalBytes": 21233664,
"scriptDurationMs": 19.885,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "canvas-idle",
"durationMs": 2012.7229999999372,
"styleRecalcs": 12,
"styleRecalcDurationMs": 10.630000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 371.086,
"heapDeltaBytes": -602828,
"domNodes": 25,
"jsHeapTotalBytes": 16134144,
"scriptDurationMs": 20.443,
"eventListeners": 30,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "canvas-idle",
"durationMs": 2007.141000000047,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.812999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 353.735,
"heapDeltaBytes": -5211940,
"domNodes": 23,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 20.769999999999996,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1827.477999999985,
"styleRecalcs": 75,
"styleRecalcDurationMs": 38.190000000000005,
"layouts": 12,
"layoutDurationMs": 3.708,
"taskDurationMs": 770.076,
"heapDeltaBytes": -2116060,
"domNodes": 59,
"jsHeapTotalBytes": 17563648,
"scriptDurationMs": 120.01900000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1861.3249999999653,
"styleRecalcs": 77,
"styleRecalcDurationMs": 37.358000000000004,
"layouts": 12,
"layoutDurationMs": 3.6689999999999996,
"taskDurationMs": 746.214,
"heapDeltaBytes": -2287800,
"domNodes": 60,
"jsHeapTotalBytes": 15990784,
"scriptDurationMs": 123.03999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1840.8850000000712,
"styleRecalcs": 78,
"styleRecalcDurationMs": 43.38,
"layouts": 12,
"layoutDurationMs": 4.154,
"taskDurationMs": 778.7420000000001,
"heapDeltaBytes": -2505748,
"domNodes": 61,
"jsHeapTotalBytes": 17563648,
"scriptDurationMs": 126.42799999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1746.1289999999963,
"styleRecalcs": 31,
"styleRecalcDurationMs": 16.408,
"layouts": 6,
"layoutDurationMs": 0.6020000000000001,
"taskDurationMs": 310.14300000000003,
"heapDeltaBytes": 6165816,
"domNodes": 77,
"jsHeapTotalBytes": 16777216,
"scriptDurationMs": 23.379,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1755.2820000000793,
"styleRecalcs": 33,
"styleRecalcDurationMs": 20.133000000000003,
"layouts": 6,
"layoutDurationMs": 0.551,
"taskDurationMs": 320.247,
"heapDeltaBytes": 6735640,
"domNodes": 83,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 24.180000000000003,
"eventListeners": 43,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1712.1250000000146,
"styleRecalcs": 32,
"styleRecalcDurationMs": 17.034,
"layouts": 6,
"layoutDurationMs": 0.5279999999999998,
"taskDurationMs": 298.35499999999996,
"heapDeltaBytes": -486264,
"domNodes": 83,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 24.371999999999996,
"eventListeners": 43,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "dom-widget-clipping",
"durationMs": 599.7710000000325,
"styleRecalcs": 13,
"styleRecalcDurationMs": 8.617,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 363.72700000000003,
"heapDeltaBytes": 13712288,
"domNodes": 22,
"jsHeapTotalBytes": 13631488,
"scriptDurationMs": 57.946,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "dom-widget-clipping",
"durationMs": 542.3710000000028,
"styleRecalcs": 13,
"styleRecalcDurationMs": 8.964,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 338.194,
"heapDeltaBytes": 11871608,
"domNodes": 21,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 57.948,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "dom-widget-clipping",
"durationMs": 545.2259999999569,
"styleRecalcs": 14,
"styleRecalcDurationMs": 10.241,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 341.716,
"heapDeltaBytes": 13164664,
"domNodes": 25,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 61.254999999999995,
"eventListeners": 26,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "large-graph-idle",
"durationMs": 1991.0380000000032,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.306999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 485.344,
"heapDeltaBytes": -9817912,
"domNodes": 22,
"jsHeapTotalBytes": 7196672,
"scriptDurationMs": 87.256,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-idle",
"durationMs": 2021.176999999966,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.047999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 493.81800000000004,
"heapDeltaBytes": -9870604,
"domNodes": 25,
"jsHeapTotalBytes": 7196672,
"scriptDurationMs": 89.92100000000002,
"eventListeners": 30,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000073
},
{
"name": "large-graph-idle",
"durationMs": 2032.5060000000121,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.519,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 489.96799999999996,
"heapDeltaBytes": -10597856,
"domNodes": 22,
"jsHeapTotalBytes": 6959104,
"scriptDurationMs": 89.447,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "large-graph-pan",
"durationMs": 2121.0629999999924,
"styleRecalcs": 71,
"styleRecalcDurationMs": 17.669,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1114.0810000000001,
"heapDeltaBytes": 7854160,
"domNodes": 22,
"jsHeapTotalBytes": 11333632,
"scriptDurationMs": 446.782,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "large-graph-pan",
"durationMs": 2085.935999999947,
"styleRecalcs": 70,
"styleRecalcDurationMs": 16.262,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1014.756,
"heapDeltaBytes": 1500148,
"domNodes": 20,
"jsHeapTotalBytes": 9236480,
"scriptDurationMs": 370.051,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "large-graph-pan",
"durationMs": 2097.593999999958,
"styleRecalcs": 71,
"styleRecalcDurationMs": 18.321000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1016.1980000000002,
"heapDeltaBytes": 5623984,
"domNodes": 22,
"jsHeapTotalBytes": 7925760,
"scriptDurationMs": 380.853,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 1990.694000000019,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.352000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 475.86400000000003,
"heapDeltaBytes": -10472708,
"domNodes": 20,
"jsHeapTotalBytes": 8245248,
"scriptDurationMs": 85.736,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "minimap-idle",
"durationMs": 2003.7880000000996,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.545,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 488.86800000000005,
"heapDeltaBytes": -10392120,
"domNodes": 20,
"jsHeapTotalBytes": 9056256,
"scriptDurationMs": 90.729,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.660000000000036
},
{
"name": "minimap-idle",
"durationMs": 1992.3639999999523,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.738,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 478.875,
"heapDeltaBytes": -10506720,
"domNodes": 24,
"jsHeapTotalBytes": 6934528,
"scriptDurationMs": 87.262,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.659999999999947
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 538.4980000000041,
"styleRecalcs": 48,
"styleRecalcDurationMs": 11.155999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 348.12299999999993,
"heapDeltaBytes": 11820676,
"domNodes": 22,
"jsHeapTotalBytes": 15990784,
"scriptDurationMs": 117.56,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 546.0130000000163,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.690999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 352.15999999999997,
"heapDeltaBytes": 11754484,
"domNodes": 22,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 119.468,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 510.87599999993927,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.007,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 336.785,
"heapDeltaBytes": 12980164,
"domNodes": 22,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 114.329,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "subgraph-idle",
"durationMs": 1995.4649999999674,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.655999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 352.83400000000006,
"heapDeltaBytes": 2116308,
"domNodes": 22,
"jsHeapTotalBytes": 17301504,
"scriptDurationMs": 16.549,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.680000000000017
},
{
"name": "subgraph-idle",
"durationMs": 1992.256999999995,
"styleRecalcs": 13,
"styleRecalcDurationMs": 11.405999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 369.307,
"heapDeltaBytes": 1693528,
"domNodes": 26,
"jsHeapTotalBytes": 17301504,
"scriptDurationMs": 21.121000000000002,
"eventListeners": 30,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "subgraph-idle",
"durationMs": 1999.1360000000213,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.016,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 357.82599999999996,
"heapDeltaBytes": 1764272,
"domNodes": 18,
"jsHeapTotalBytes": 17825792,
"scriptDurationMs": 19.009999999999998,
"eventListeners": 28,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1725.5150000000299,
"styleRecalcs": 79,
"styleRecalcDurationMs": 41.68,
"layouts": 16,
"layoutDurationMs": 4.368,
"taskDurationMs": 656.0989999999999,
"heapDeltaBytes": -6933288,
"domNodes": 68,
"jsHeapTotalBytes": 17039360,
"scriptDurationMs": 89.339,
"eventListeners": 28,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1731.029000000035,
"styleRecalcs": 79,
"styleRecalcDurationMs": 40.738,
"layouts": 16,
"layoutDurationMs": 4.843,
"taskDurationMs": 687.6049999999999,
"heapDeltaBytes": -7182828,
"domNodes": 65,
"jsHeapTotalBytes": 16777216,
"scriptDurationMs": 96.232,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000027
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1738.5189999999966,
"styleRecalcs": 80,
"styleRecalcDurationMs": 39.977,
"layouts": 16,
"layoutDurationMs": 4.593,
"taskDurationMs": 672.698,
"heapDeltaBytes": -6438816,
"domNodes": 67,
"jsHeapTotalBytes": 17301504,
"scriptDurationMs": 89.892,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.67999999999997
},
{
"name": "vue-large-graph-idle",
"durationMs": 11161.60400000001,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11152.243,
"heapDeltaBytes": -24838380,
"domNodes": -3333,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 566.4350000000001,
"eventListeners": -16488,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.33000000000029
},
{
"name": "vue-large-graph-idle",
"durationMs": 11610.646000000088,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11592.517999999998,
"heapDeltaBytes": -31060456,
"domNodes": -3339,
"jsHeapTotalBytes": 22544384,
"scriptDurationMs": 616.409,
"eventListeners": -16486,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.340000000000146
},
{
"name": "vue-large-graph-idle",
"durationMs": 11321.045000000027,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 11309.800000000001,
"heapDeltaBytes": -26591968,
"domNodes": -3331,
"jsHeapTotalBytes": 21757952,
"scriptDurationMs": 605.8939999999999,
"eventListeners": -16486,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.339999999999783
},
{
"name": "vue-large-graph-pan",
"durationMs": 13200.69000000001,
"styleRecalcs": 65,
"styleRecalcDurationMs": 13.191000000000008,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13182.626999999999,
"heapDeltaBytes": -10898172,
"domNodes": -3338,
"jsHeapTotalBytes": 17477632,
"scriptDurationMs": 844.973,
"eventListeners": -16484,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.329999999999927
},
{
"name": "vue-large-graph-pan",
"durationMs": 13469.667999999956,
"styleRecalcs": 65,
"styleRecalcDurationMs": 13.689000000000007,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13449.192000000001,
"heapDeltaBytes": -42209352,
"domNodes": -3889,
"jsHeapTotalBytes": 19312640,
"scriptDurationMs": 884.654,
"eventListeners": -16496,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.329999999999927
},
{
"name": "vue-large-graph-pan",
"durationMs": 13173.531000000025,
"styleRecalcs": 65,
"styleRecalcDurationMs": 13.324000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13155.437000000002,
"heapDeltaBytes": -17376260,
"domNodes": -3338,
"jsHeapTotalBytes": 23244800,
"scriptDurationMs": 858.4040000000001,
"eventListeners": -16484,
"totalBlockingTimeMs": 57,
"frameDurationMs": 18.339999999999783
},
{
"name": "workflow-execution",
"durationMs": 444.5460000000594,
"styleRecalcs": 17,
"styleRecalcDurationMs": 23.586,
"layouts": 5,
"layoutDurationMs": 1.6059999999999999,
"taskDurationMs": 137.89800000000002,
"heapDeltaBytes": -14319304,
"domNodes": 164,
"jsHeapTotalBytes": 7864320,
"scriptDurationMs": 31.247000000000003,
"eventListeners": 55,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "workflow-execution",
"durationMs": 464.5910000000413,
"styleRecalcs": 23,
"styleRecalcDurationMs": 25.586,
"layouts": 6,
"layoutDurationMs": 1.601,
"taskDurationMs": 131.863,
"heapDeltaBytes": 4500648,
"domNodes": 179,
"jsHeapTotalBytes": 4194304,
"scriptDurationMs": 29.375000000000004,
"eventListeners": 55,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.65999999999999
},
{
"name": "workflow-execution",
"durationMs": 439.3129999999701,
"styleRecalcs": 19,
"styleRecalcDurationMs": 22.788000000000004,
"layouts": 5,
"layoutDurationMs": 1.2529999999999997,
"taskDurationMs": 114.072,
"heapDeltaBytes": 4328192,
"domNodes": 157,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 23.756000000000007,
"eventListeners": 55,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998
}
]
} |
Summary
Add historical trend visualization (ASCII sparklines + directional arrows) to the performance PR report, showing how each metric has moved over recent commits on main.
Changes
sparkline(),trendDirection(),trendArrow()functions inperf-stats.ts. New collapsible "Trend" section in the perf report showing per-metric sparklines, direction indicators, and latest values. CI workflow updated to download historical data from theperf-dataorphan branch and switched tosetup-frontendaction withpnpm exec tsx.Review Focus
trendDirection()uses a split-half mean comparison with ±10% threshold — review whether this sensitivity is appropriategit archivestep inpr-perf-report.yamlis idempotent and fails silently if no perf-history data exists yet on the perf-data branch┆Issue is synchronized with this Notion page by Unito